package util

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math/rand"
	"os"
	"strconv"
)

//////////////////////////////////////////////////////////////////////
// Data types
//////////////////////////////////////////////////////////////////////

func IntToString(num int) string {
	return strconv.Itoa(num)
}

func StringToInt(str string) int {
	val, _ := strconv.ParseInt(str, 10, 32)
	return int(val)
}

func StringToUint32(str string) uint32 {
	val, _ := strconv.ParseUint(str, 10, 32)
	return uint32(val)
}

func StringToUint64(str string) uint64 {
	val, _ := strconv.ParseUint(str, 10, 64)
	return val
}

func StringToFloat32(str string) float32 {
	val, _ := strconv.ParseFloat(str, 32)
	return float32(val)
}

func StringToFloat64(str string) float64 {
	val, _ := strconv.ParseFloat(str, 64)
	return val
}

func InterfaceToInt(arg interface{}) (num int, err error) {
	switch val := arg.(type) {
	case uint8:
		num = int(val)
	case uint16:
		num = int(val)
	case uint32:
		num = int(val)
	case uint64:
		num = int(val)
	case int8:
		num = int(val)
	case int16:
		num = int(val)
	case int32:
		num = int(val)
	case int64:
		num = int(val)
	case uint:
		num = int(val)
	case int:
		num = val
	default:
		err = errors.New("incompatible type")
	}

	return
}

func InterfaceToFileMode(arg interface{}) (mode os.FileMode, err error) {
	num, err := InterfaceToInt(arg)
	if err != nil {
		return
	}

	mode = os.FileMode(num)
	return
}

func CalcUint64Diff(a, b uint64) uint64 {
	if b > a {
		return b - a
	} else {
		return 0
	}
}

func PackUint32LE(num uint32) []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.LittleEndian, num)
	return buf.Bytes()
}

func PackUint32BE(num uint32) []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, num)
	return buf.Bytes()
}

func UnpackUint32LE(data []byte) uint32 {
	return binary.LittleEndian.Uint32(data)
}

func UnpackUint32BE(data []byte) uint32 {
	return binary.BigEndian.Uint32(data)
}

func NewRandString(option_chars string, length int) string {
	option_runes := []rune(option_chars)
	rune_count := len(option_runes)
	buf := make([]rune, length)
	for i := range buf {
		idx := rand.Intn(rune_count)
		buf[i] = option_runes[idx]
	}

	return string(buf)
}

func NewRandBytes(option_bytes []byte, length int) []byte {
	byte_count := len(option_bytes)
	buf := make([]byte, length)
	for i := range buf {
		idx := rand.Intn(byte_count)
		buf[i] = option_bytes[idx]
	}

	return buf
}

//////////////////////////////////////////////////////////////////////
// Math
//////////////////////////////////////////////////////////////////////

func ClampInt(num int, min int, max int) int {
	if num < min {
		return min
	} else if num > max {
		return max
	} else {
		return num
	}
}

func ClampFloat64(num float64, min float64, max float64) float64 {
	if num < min {
		return min
	} else if num > max {
		return max
	} else {
		return num
	}
}

// Min returns minimum of two ints
func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
